/**
 * 数据库操作辅助模块
 * 提供统一的数据库查询接口，封装了错误处理、参数验证和 SQL 注入防护
 * 
 * @module db-helper
 * @author DuYu
 * @date 2025-10-20
 * @version 1.1.0
 */

import { pool } from '../db/index.js';

/**
 * SQL 操作类型枚举
 * @readonly
 * @enum {string}
 */
const SQL_OPERATION_TYPES = {
  SELECT: 'SELECT',
  INSERT: 'INSERT',
  UPDATE: 'UPDATE',
  DELETE: 'DELETE',
  EXECUTE: 'EXECUTE'
};

/**
 * 验证 SQL 语句合法性
 * @param {string} sql - 待验证的 SQL 语句
 * @returns {void}
 * @throws {Error} 当 SQL 语句存在安全风险时抛出错误
 */
function validateSQLStatement(sql) {
  // 检查是否包含危险的 SQL 关键字
  const dangerousKeywords = ['DROP', 'TRUNCATE', 'ALTER', 'SHOW', 'GRANT', 'REVOKE', 'CREATE', 'INDEX'];
  const sqlUpperCase = sql.toUpperCase();
  
  // 检查是否包含多条 SQL 语句（分号分隔）
  if (sqlUpperCase.split(';').length > 1) {
    throw new Error('不允许执行多条 SQL 语句');
  }
  
  // 可以根据需要启用关键字检查，但注意这可能会限制合法的 SQL 操作
  // dangerousKeywords.forEach(keyword => {
  //   if (sqlUpperCase.includes(keyword)) {
  //     throw new Error(`不允许使用 ${keyword} 关键字`);
  //   }
  // });
}

/**
 * 执行数据库 SQL 查询
 * 
 * @async
 * @function query
 * @param {string} sql - SQL 查询语句
 * @param {Array} [params=[]] - SQL 查询参数（可选）
 * @returns {Promise<Array>} 查询结果集数组
 * @throws {Error} 当 SQL 不是字符串类型、参数格式错误或数据库查询失败时抛出错误
 * 
 * @example
 * // 基本查询示例
 * const users = await query('SELECT * FROM users');
 * 
 * @example
 * //带参数的查询示例 - 推荐使用参数化查询防止 SQL 注入
 * const user = await query('SELECT * FROM users WHERE id = ?', [1]);
 */
async function query(sql, params = []) {
  // 参数验证：确保 SQL 语句是字符串类型
  if (typeof sql !== 'string') {
    throw new Error('SQL 语句必须是字符串类型');
  }
  
  // 参数验证：确保 params 是数组类型
  if (!Array.isArray(params)) {
    throw new Error('查询参数必须是数组类型');
  }
  
  // SQL 语句安全验证
  validateSQLStatement(sql);

  try {
    // 使用参数化查询防止 SQL 注入
    const [rows] = await pool.query(sql, params);
    return rows;
  } catch (error) {
    // 增强错误日志，但避免泄露敏感信息
    console.error('数据库查询错误:', {
      // 不记录完整 SQL 语句，只记录操作类型
      operationType: sql.trim().split(' ')[0].toUpperCase(),
      // 不记录参数具体值，只记录参数数量
      paramsCount: params.length,
      error: error.message
    });
    
    // 重新抛出错误，以便上层调用者能够处理
    throw error;
  }
}

/**
 * 安全执行查询操作的包装函数
 * 额外提供 SQL 操作类型限制和更严格的参数验证
 * 
 * @async
 * @function safeQuery
 * @param {string} operationType - SQL 操作类型
 * @param {string} sql - SQL 查询语句
 * @param {Array} [params=[]] - SQL 查询参数
 * @returns {Promise<Array>} 查询结果集数组
 * @throws {Error} 当操作类型不允许、SQL 语句或参数不合法时抛出错误
 */
async function safeQuery(operationType, sql, params = []) {
  // 验证操作类型是否允许
  const allowedTypes = Object.values(SQL_OPERATION_TYPES);
  if (!allowedTypes.includes(operationType.toUpperCase())) {
    throw new Error(`不允许的 SQL 操作类型: ${operationType}`);
  }
  
  // 验证 SQL 语句是否符合指定的操作类型
  if (!sql.toUpperCase().startsWith(operationType.toUpperCase())) {
    throw new Error(`SQL 语句与指定的操作类型不匹配: ${operationType}`);
  }
  
  // 调用主查询函数
  return await query(sql, params);
}

export { query, safeQuery, SQL_OPERATION_TYPES };
